import numpy as np
import tensorflow as tf
from tensorflow.keras import backend as bk
import sklearn.metrics as lm
import pandas as pd

def precision(y_true, y_pred):
    y_pred_1 = bk.round(y_pred)
    y_true_1 = bk.round(y_true)
    TP = bk.sum(y_pred_1*y_true_1)
    TP_AND_FP = bk.sum(y_pred_1)
    return TP/(TP_AND_FP+bk.epsilon())

def recall(y_true, y_pred):
    y_pred_1 = bk.round(y_pred)
    y_true_1 = bk.round(y_true)
    TP = bk.sum(y_pred_1*y_true_1)
    TP_AND_FN = bk.sum(y_true_1)
    return TP/(TP_AND_FN+bk.epsilon())
    
def AUC(y_true,  y_pred):
    TPR = tf.stack([TPR_threshold(y_true, y_pred, k) for k in np.linspace(0,1,1000)], axis=0)
    FPR = tf.stack([FPR_threshold(y_true, y_pred, k) for k in np.linspace(0,1,1000)], axis=0)
    TPR = TPR[1:]
    FPR = FPR[:-1] - FPR[1:]
    return bk.sum(TPR*FPR)

def accuracy(y_true, y_pred):
    y_pred_1 = bk.round(y_pred)
    y_true_1 = bk.round(y_true)
    T = bk.sum((1-y_true_1)*(1-y_pred_1) + (y_pred_1*y_true_1))
    A = y_true_1.shape[0]
    return T/A

def TPR_threshold(y_true, y_pred, threshold=bk.variable(value=0.5)):
    y_pred_1 = bk.cast(y_pred>=threshold, "float32")
    return TPR(y_true, y_pred_1)
    
def FPR_threshold(y_true, y_pred, threshold=bk.variable(value=0.5)):
    y_pred_1 = bk.cast(y_pred>=threshold, "float32")
    return FPR(y_true, y_pred_1)

#true positive rate(sensitivity)
def TPR(y_true, y_pred):
    return recall(y_true, y_pred)
    
#false positive rate
def FPR(y_true, y_pred):
    y_pred_1 = bk.round(y_pred)
    y_true_1 = bk.round(y_true)
    FP = bk.sum(y_pred_1-(y_true_1*y_pred_1))
    FP_AND_TN = bk.sum(1-y_true_1)
    return FP/(FP_AND_TN+bk.epsilon())

#true negetive rate(specificity)
def TNR(y_true, y_pred):
    y_pred_1 = bk.round(y_pred)
    y_true_1 = bk.round(y_true)
    y_pred_1 = 1 - y_pred_1
    y_true_1 = 1 - y_true_1
    TN = bk.sum(y_true_1*y_pred_1)
    FP_AND_TN = bk.sum(y_true_1)
    return TN/(FP_AND_TN+bk.epsilon())

#F1-score
def F1(y_true, y_pred):
    P = precision(y_true, y_pred)
    R = recall(y_true, y_pred)
    return (2*P*R)/(P+R+bk.epsilon())

#pr
def pr_curve(y_true, y_pred, save_path):
    precision, recall, thresholds = lm.precision_recall_curve(y_true, y_pred)
    precision = np.array(precision).reshape(len(precision), 1)
    recall = np.array(recall).reshape(len(recall), 1)
    pr = np.concatenate([recall, precision], axis=1)
    pr = pd.DataFrame(pr, columns=["R", "P"])
    pr.to_csv(save_path)

#roc
def roc_curve(y_true, y_pred, save_path):
    fpr, tpr, thresholds = lm.roc_curve(y_true, y_pred)
    fpr = np.array(fpr).reshape(len(fpr), 1)
    tpr = np.array(tpr).reshape(len(tpr), 1)
    roc = np.concatenate([fpr, tpr], axis=1)
    roc = pd.DataFrame(roc, columns=["FPR", "TPR"])
    roc.to_csv(save_path)

def dis(y_true, y_pred):
    distance = TPR(y_true, y_pred) - TNR(y_true, y_pred)
    if distance < 0:
        distance = 0 - distance
    return distance